package com.gadgets.cm04.fileoper.controller;

import com.gadgets.cm04.common.GadgetsCm04Constant;
import com.gadgets.cm04.common.GadgetsCm04ExceptionConstant;
import com.gadgets.cm04.fileregix.pojo.FileRegixInfo;
import com.gadgets.framework.annotation.IgnoreStandardResp;
import com.gadgets.framework.common.constant.GadgetsExceptionConstant;
import com.gadgets.framework.common.exception.GadgetsServiceException;
import com.gadgets.framework.common.exception.GadgetsValidateException;
import com.gadgets.framework.common.util.CacheUtils;
import com.gadgets.framework.common.util.StringUtils;
import com.gadgets.framework.fileload.FileSuffixConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @author UncleXiu
 * @version V1.0.0
 * @data 2020-09-16
 * 
 */
@Controller
@RequestMapping(method = {org.springframework.web.bind.annotation.RequestMethod.POST,org.springframework.web.bind.annotation.RequestMethod.GET},value = "/operFile")
//@RoleAuthCheck
public class FileOperationController implements IFileOperationController{

    private static final Logger logger = LoggerFactory.getLogger(FileOperationController.class);

    /**
     * 文件上传基础路径
     */
    @Value("${spring.uploadPath}")
    private String uploadPath;
    @Autowired
    private CacheUtils cacheUtils;

    /**
     * 文件上传
     * @param request   请求信息
     * @param response  应答信息
     * @param file      上传文件
     * @return          文件序列号
     */
    @RequestMapping(method = {org.springframework.web.bind.annotation.RequestMethod.POST,org.springframework.web.bind.annotation.RequestMethod.GET},value = "/upload.do")
    @ResponseBody
    @Override
    //@RoleAuthCheck(ifLogin = false)
    @IgnoreStandardResp
    public String fileUpload(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "file",required = false) MultipartFile file){
        String fileName = file.getOriginalFilename();
        if(!StringUtils.isBlank(fileName)) {
            int suffixDot = 0;
            String suffix = null;
            // 获取文件后缀
            if ((suffixDot = fileName.lastIndexOf(FileSuffixConstant.FILE_SUFFIX_DOT)) > -1) {
                suffix = fileName.substring(suffixDot);
            }
            fileUploadFileCheck(fileName,suffix);
            // 为上传文件设置新文件名，避免攻击者由外部调用文件或脚本
            String fileuuid =UUID.randomUUID().toString();
            File destFile = new File(uploadPath+"/"+fileuuid+suffix);
            try {
                file.transferTo(destFile);
            } catch (IOException e) {
                logger.error("文件上传失败到指定路径失败，失败原因{}",e.getMessage());
                logger.error(StringUtils.formatExceptionStack(e));
                throw new GadgetsServiceException(GadgetsCm04ExceptionConstant.FILE_UPLOAD_ERROR);
            }
            // 返回文件序列
            return fileuuid;
        }else {
            throw new GadgetsValidateException(GadgetsExceptionConstant.VALIDATE_ERROR,"文件名不能为空");
        }
    }

    /**
     * 文件下载
     * @param request   请求信息
     * @param response  应答信息
     * @param fileuuid  文件序列号
     */
    @RequestMapping(method = {org.springframework.web.bind.annotation.RequestMethod.POST,org.springframework.web.bind.annotation.RequestMethod.GET},value = "/download.do")
    @ResponseBody
    @Override
    //@RoleAuthCheck(ifLogin = false)
    @IgnoreStandardResp
    public void fileDownload(HttpServletRequest request, HttpServletResponse response, String fileuuid){
        File file = new File(uploadPath+"/"+fileuuid);
        try {
            FileInputStream inputStream = new FileInputStream(file);
            response.setContentType("application/force-download");
            response.addHeader("Content-disposition","attachment;fileName="+file.getName());
            OutputStream os = response.getOutputStream();
            byte[] buf = new byte[2048];
            int leng = 0;
            while ((leng = inputStream.read(buf))!= -1){
                os.write(buf, 0 , leng);
            }
            os.flush();
        } catch (IOException e) {
            logger.error(StringUtils.formatExceptionStack(e));
            throw new GadgetsServiceException(GadgetsCm04ExceptionConstant.FILE_DOWNLOAD_ERROR,e.getMessage());
        }
    }

    /**
     * 文件上传校验
     * @param fileName                      文件名
     * @param suffix                        文件类型
     * @throws GadgetsValidateException     校验错误信息
     */
    @SuppressWarnings("unchecked")
    protected void fileUploadFileCheck(String fileName, String suffix) throws GadgetsValidateException{
        List<FileRegixInfo> uploadRegixs = cacheUtils.getDefaultCache(GadgetsCm04Constant.FILE_UPLOAD_CACHE_KEY, ArrayList.class);
        if(null==uploadRegixs){
            return;
        }
        boolean whiteCheck = "W".equals(cacheUtils.getSysParamCache(GadgetsCm04Constant.UPLOAD_REGIX_RULE));
        boolean whiteResult = !whiteCheck;
        boolean regixResult;
        for (FileRegixInfo uploadRegix : uploadRegixs) {
            if(!whiteCheck && GadgetsCm04Constant.REGIX_TYPE_CONSTANT.REGIX_UPLOAD_BLACK.equals(uploadRegix.getRegixType())){
                // 黑名单校验模式下只要命中任意一条规则就抛出异常
                if (GadgetsCm04Constant.REGIX_SCOPE_FILENAME.equals(uploadRegix.getScope())) {
                    regixResult = fileName.contains(uploadRegix.getRegix());
                } else {
                    regixResult = suffix.equals(uploadRegix.getRegix());
                }
                // 如果命中了校验规则，则抛出异常
                if (regixResult) {
                    throw new GadgetsServiceException(GadgetsCm04ExceptionConstant.FILE_REGIX_ERROR, uploadRegix.getRegix());
                }
            }else if(whiteCheck && GadgetsCm04Constant.REGIX_TYPE_CONSTANT.REGIX_UPLOAD_WHITE.equals(uploadRegix.getRegixType())){
                // 白名单校验模式下只要命中任意一条规则就无需再继续校验
                if(GadgetsCm04Constant.REGIX_SCOPE_FILENAME.equals(uploadRegix.getScope())){
                    whiteResult = fileName.contains(uploadRegix.getRegix());
                }else{
                    whiteResult = suffix.equals(uploadRegix.getRegix());
                }
                if(whiteResult){
                    whiteCheck = false;
                }
            }
        }
        // 如果没有命中任意一条白名单则报错
        if(!whiteResult){
            throw new GadgetsServiceException(GadgetsCm04ExceptionConstant.FIEL_REGIX_WHITE_ERROR, fileName, suffix);
        }
    }

}
